package com.xkcoding.orm.jpa.utils;

import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

import static java.util.stream.Collectors.toList;

/**
 * 实体转换DTO
 *
 * @author wuyue
 * @since 1.0, 2022-03-16
 */
public class DtoTransformer {

    private DtoTransformer() {
        throw new IllegalArgumentException("No DtoTransformer instance for you.");
    }

    /**
     * 将实体列表转为dto实例列表
     *
     * @param rClazz dto类
     * @param entityListFun 获取实体列表的函数
     * @param funParam 函数参数
     * @return dto实例列表
     */
    public static <R, E> List<R> list(Class<R> rClazz, Function<String, List<E>> entityListFun,
                                      String funParam) {
        List<E> entities = entityListFun.apply(funParam);
        return entities.stream().map(entity -> single(rClazz, entity)).collect(toList());
    }

    /**
     * 将实体列表转为dto实例列表
     *
     * @param rClazz dto类
     * @param entityListFun 获取实体列表的函数
     * @param funParams 函数参数
     * @return dto实例列表
     */
    public static <R, E> List<R> list(Class<R> rClazz, Function<String[], List<E>> entityListFun,
                                      String... funParams) {
        List<E> entities = entityListFun.apply(funParams);
        return entities.stream().map(entity -> single(rClazz, entity)).collect(toList());
    }

    /**
     * 将实体列表转为dto实例列表
     *
     * @param rClazz dto类
     * @param entityListFun 获取实体列表的函数
     * @param funParams 函数参数
     * @return dto实例列表
     */
    public static <R, E> List<R> list(Class<R> rClazz, Function<List<String>, List<E>> entityListFun,
                                      List<String> funParams) {
    	List<E> entities = entityListFun.apply(funParams);
    	return entities.stream().map(entity -> single(rClazz, entity)).collect(toList());
    }

    /**
     * 将实体列表转为dto实例列表
     *
     * @param entityListFun 获取实体列表的函数
     * @param funParam 函数参数
     * @param entityTransFun 单个实体转为dto实例的函数
     * @return dto实例列表
     */
    public static <R, E> List<R> list(Function<String, List<E>> entityListFun,
                                      String funParam, Function<E, R> entityTransFun) {
        List<E> entities = entityListFun.apply(funParam);
        return entities.stream().map(entityTransFun).collect(toList());
    }

    /**
     * 将实体列表转为dto实例列表
     *
     * @param entityListFun 获取实体列表的函数
     * @param funParams 函数参数
     * @param entityTransFun 单个实体转为dto实例的函数
     * @return dto实例列表
     */
    public static <R, E> List<R> list(Function<String[], List<E>> entityListFun,
                                      Function<E, R> entityTransFun, String... funParams) {
        List<E> entities = entityListFun.apply(funParams);
        return entities.stream().map(entityTransFun).collect(toList());
    }

    /**
     * 将实体转为dto实例
     *
     * @param rClazz 返回的类型
     * @param entity 实体类型
     * @param <R> 返回的类型
     * @param <E> 实体类型
     * @return dto实例
     */
    public static <R, E> R single(Class<R> rClazz, E entity) {
        Optional<E> optEntity = Optional.ofNullable(entity);
        return single(rClazz, optEntity);
    }

    /**
     * 将实体转为dto实例
     *
     * @param rClazz 返回的类型
     * @param optEntity Optional实体
     * @param <R> 返回类型
     * @param <E> 实体类型
     * @return 返回类型实例
     */
    public static <R, E> R single(Class<R> rClazz, Optional<E> optEntity) {
        return optEntity.flatMap(e -> newInstanceOpt(rClazz, e, null)).orElse(null);
    }

    /**
     * 将实体转为dto实例
     *
     * @param rClazz 返回的类型
     * @param optEntity Optional实体
     * @param consumer consumer对象
     * @param <R> 返回类型
     * @param <E> 实体类型
     * @return 返回类型实例
     */
    public static <R, E> R single(Class<R> rClazz, Optional<E> optEntity, Consumer<R> consumer) {
        return optEntity.flatMap(e -> newInstanceOpt(rClazz, e, consumer)).orElse(null);
    }

    private static <R, E> Optional<R> newInstanceOpt(Class<R> rClazz, E entity, Consumer<R> consumer) {
        try {
            R dto = rClazz.newInstance();
            BeanUtils.copyProperties(entity, dto);
            if (consumer != null) {
                consumer.accept(dto);
            }
            return Optional.of(dto);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return Optional.empty();
        }
    }

}
